<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <link rel="shortcut icon" href="">
    <title>RayCasting</title>
    <style type="text/css">
        body {
            background-color: #eccc68;
        }

        canvas {
            width: 400px;
            height: 300px;
            border: 1px solid black;
        }
    </style>
</head>

<body>
    <script type="module">
        import {createCanvas, Render_, vector2, vector28Angle} from './spring/canvas.js'

        const width = 800, height = 600

        class Boundary {
            constructor(x1, y1, x2, y2) {
                this.a = vector2(x1, y1)
                this.b = vector2(x2, y2)
            }

            show(render) {
                render.line(this.a.x, this.a.y, this.b.x, this.b.y)
                render.stroke()
            }
        }

        class Ray{
            constructor(pos, dir) {
                this.pos = pos
                this.dir = dir
            }

            show(render) {
                render.line(this.pos.x, this.pos.y, this.pos.x + this.dir.x * 20, this.pos.y + this.dir.y * 20)
                render.stroke("#ecf0f1", 2)
            }

            lookAt (x, y) {
                this.dir.x = x - this.pos.x
                this.dir.y = y - this.pos.y
                this.dir.normalize()
            }

            cast(boundary) {
                const x1 = boundary.a.x
                const y1 = boundary.a.y
                const x2 = boundary.b.x
                const y2 = boundary.b.y

                const x3 = this.pos.x
                const y3 = this.pos.y
                const x4 = this.pos.x + this.dir.x
                const y4 = this.pos.y + this.dir.y

                const den = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4)
                if (den === 0) {
                    return
                }
                const t = ((x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4)) / den
                const u = -((x1 - x2) * (y1 - y3) - (y1 - y2) * (x1 - x3)) / den
                if (t > 0 && t < 1 && u > 0) {
                    return vector2(x1 + t * (x2 - x1), y1 + t * (y2 - y1))
                }

            }
        }

        class Particle {
            constructor() {
                this.pos = vector2(width / 2, height / 2)
                this.rays = []
                for (let i = 0; i < 360; i+=.10) {
                    this.rays.push(new Ray(this.pos, vector28Angle(i)))
                }
            }

            look(walls, render) {
                this.rays.forEach( ray => {
                    let min = Infinity
                    let point = null
                    walls.forEach( wall => {
                        let pt = ray.cast(wall)
                        if (pt) {
                            let d = this.pos.clone().sub(pt).length
                            if (d < min) {
                                min = d
                                point = pt
                            }
                        }
                    })
                    if (point) {
                        render.line(this.pos.x, this.pos.y, point.x, point.y)
                        render.stroke('#fff2')
                    }
                })
            }

            move(x, y) {
                this.pos.x = x
                this.pos.y = y
            }

            show(render) {
                render.circle(this.pos.x, this.pos.y, 8, 20)
                render.fill()
                this.rays.forEach( ray => {
                    ray.show(render)
                })
            }
        }

        const render = new Render_(width / 2, height / 2)
        render.frame(10)
        let particle = new Particle()
        let walls = []
        for (let i = 0; i < 5; ++i) {
            let x1 = Math.random() * width
            let y1 = Math.random() * height
            let x2 = Math.random() * width
            let y2 = Math.random() * height
            walls.push(new Boundary(x1, y1, x2, y2))
        }
        walls.push(new Boundary(0, 0, width, 0))
        walls.push(new Boundary(width, 0, width, height))
        walls.push(new Boundary(width, height, 0, height))
        walls.push(new Boundary(0, height, 0, 0))

        let xoff = 0, yoff = 0

        function draw() {
            walls.forEach( wall => {
                wall.show(render)
            })
            particle.show(render)
            particle.look(walls, render)
            if (render.mouse.isPress) {
                particle.move(render.mouse.position.x, render.mouse.position.y)
            } else {
                particle.move(Math.sin(xoff) * width / 2 + width / 2, Math.cos(yoff) * height / 2 + height / 2)
                xoff += 0.01
                yoff += 0.01
            }
        }
        render.animation(draw)
    </script>
</body>

</html>